#include <iostream>
#include <pthread.h>
#include <time.h>
#include "EventManager.h"

using namespace std;

void *CheckEvents(void *arg);

/*!
 * Creates a new EventManager to the MoteCollection motes
 * @param[in] motes	the MoteCollection variable to observe
 */
EventManager::EventManager(MoteCollection **motes)
{
	MoteCollection *tmp = *motes;
	this->motes = tmp;

	nextEventID = 1;

	pthread_t id;
	int err = pthread_create(&id, NULL, CheckEvents, this);

	if(err != 0)
	{
		cout << "Can't create thread " << strerror(err) << endl;
	}
}

EventManager::~EventManager()
{
}

/*!
 * Registers a new event and adds it to the event queue
 * \param[in] times The number of times to execute this event (-1 for infinity)
 * \param[in] moteID The mote in which this event belongs to (-1 for all motes)
 * \param[in] cond The condition that triggers this event depending on the value of "value". Can be one of the following: HIGHER, LOWER, EXACT
 * \param[in] value The value that triggers this event. This also can be LIGHT or DARK instead of a specific value.
 * \param[in] offset The offset of the value that triggers this event. For example, if value = 10 and offset = 2, then triggering value will be between 8 and 12
 * \param[in] function The function to execute if the condition cond and value are met. The signature of the function should be in the format (void *, Mote *) where the first argument is described next, and Mote is a pointer to the Mote that triggered the event
 * \param[in] arg A user defined argument to be passed to the function
 * \return the ID of the event
 */
int EventManager::RegisterEvent(int times, int moteID, Condition cond, int value, int offset, void (*function)(void *, Mote *), void *arg)
{
	Event e;
	e.id = nextEventID;
	nextEventID++;
	e.moteID = moteID;
	e.times = times;
	e.cond = cond;
	e.value = value;
	e.offset = offset;
	e.function = function;
	e.arg = arg;

	ActiveEvents.push_back(e);

	return e.id;	
}

/**
 * Removes an event from the event queue
 * \param[in] eventID the event ID to be deleted
 * \return Whether the event is deleted or not
 */
bool EventManager::DeleteEvent(int eventID)
{
	for(vector<Event>::iterator i = ActiveEvents.begin(); i != ActiveEvents.end(); i++ )
	{
		if(i->id == eventID)
		{
			ActiveEvents.erase(i);
			return SUCCESS;
		}	
	}
}

void *CheckEvents(void *arg)
{
	EventManager *e = (EventManager *)arg;
	int moteID;

	while(true) //always evaluate events
	{
		for(int i = 0; i < e->ActiveEvents.size(); i++ )
		{
			if(e->EvalEvent(&e->ActiveEvents.at(i), &moteID) == SUCCESS)
			{
				e->ActiveEvents.at(i).function(e->ActiveEvents.at(i).arg, e->motes->Motes()[i]);

				if(e->ActiveEvents.at(i).times != -1)
				{
					e->ActiveEvents.at(i).times--;
				}
			}

			if(e->ActiveEvents.at(i).times == 0) //remove event if times = 0
			{
				e->ActiveEvents.erase(e->ActiveEvents.begin() + i);
			}
		}
	}	
}

bool EventManager::EvalEvent(Event *e, int *moteID)
{
	if(e->times == 0)
	{
		return FAILURE;
	}

	int value;

	if(e->value == DARK)
	{
		value = motes->DarkLevel();
	}
	else if(e->value == LIGHT)
	{
		value = motes->LightLevel();
	}
	else //specific reading
	{
		value = e->value;
	}

	if(e->moteID == -1) //all motes
	{
		for(int i = 0 ; i < motes->Motes().size(); i ++)
		{
			int reading = motes->Motes()[i]->Reading();

			if(e->cond == EXACT)
			{
				if(reading <= value + e->offset && reading >= value - e->offset)
				{
					*moteID = i;
					return SUCCESS;
				}
			}
			else if(e->cond == HIGHER)
			{
				if(reading >= value || reading >= value + e->offset || reading >= value - e->offset)
				{
					*moteID = i;
					return SUCCESS;
				}
			}
			else if(e->cond == LOWER)
			{
				if(reading <= value || reading <= value + e->offset || reading <= value - e->offset)
				{
					*moteID = i;
					return SUCCESS;
				}
			}
		}
	}
	else //specific mote
	{
		if(motes->Motes().size() <= motes->MotePosition(e->moteID)) //mote is not yet registered
		{
			return FAILURE;
		}

		*moteID = e->moteID;

		int reading = motes->Motes()[motes->MotePosition(e->moteID)]->Reading();

		if(e->cond == EXACT)
		{
			if(reading <= value + e->offset && reading >= value - e->offset)
			{
				return SUCCESS;
			}
		}
		else if(e->cond == HIGHER)
		{
			if(reading >= value || reading >= value + e->offset || reading >= value - e->offset)
			{
				return SUCCESS;
			}
		}
		else if(e->cond == LOWER)
		{
			if(reading <= value || reading <= value + e->offset || reading <= value - e->offset)
			{
				return SUCCESS;
			}
		}
	}

	return FAILURE;
}
